package algorithm.recursionAndbacktrack.twoDimensions.floodfill.p417;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 417. 太平洋大西洋水流问题
 */
class Solution_v1 {
    private int m;
    private int n;
    private int[][] d = {
            {-1, 0}, // 上
            {0, 1},  // 右
            {1, 0},  // 下
            {0, -1}  // 左
    };

    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        m = heights.length;
        n = heights[0].length;
        List<List<Integer>> res_pacific = new ArrayList<>();
        List<List<Integer>> res_atlantic = new ArrayList<>();
        boolean[][] visited = new boolean[m][n];
        // 从陆地边缘进行深度优先搜索，标记高度递增的地形
        // 太平洋
        // 纵向
        for (int i = 0; i < m; i++) {
            if (!visited[i][0]) {
                dfs(heights, visited, res_pacific, i, 0, 0);
            }
        }

        // 横向
        for (int j = 0; j < n; j++) {
            if (!visited[0][j]) {
                dfs(heights, visited, res_pacific, 0, j, 0);
            }
        }

        // visited数组复位
        for (int i = 0; i < m; i++) {
            Arrays.fill(visited[i], false);
        }

        // 大西洋
        // 纵向
        for (int j = 0; j < n; j++) {
            if (!visited[m - 1][j]) {
                dfs(heights, visited, res_atlantic, m - 1, j, 0);
            }
        }
        // 横向
        for (int i = 0; i < m; i++) {
            if (!visited[i][n - 1]) {
                dfs(heights, visited, res_atlantic, i, n - 1, 0);
            }
        }

        // 求交集，存到第一个集合中
        res_pacific.retainAll(res_atlantic);
        return res_pacific;
    }

    private void dfs(int[][] heights, boolean[][] visited, List<List<Integer>> res, int x, int y, int lastHeight) {
        // 如果当前小区的高度不低于相邻小区的高度
        if (heights[x][y] >= lastHeight) {
            // 只有满足条件的才会被标记，下次就不会被遍历到了
            visited[x][y] = true;
            res.add(Arrays.asList(x, y));
        } else return;

        // 往四个方向搜索
        for (int i = 0; i < d.length; i++) {
            int newX = x + d[i][0];
            int newY = y + d[i][1];
            if (inArea(newX, newY) && !visited[newX][newY]) {
                dfs(heights, visited, res, newX, newY, heights[x][y]);
            }
        }
    }

    private boolean inArea(int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }

    public static void main(String[] args) {
        int[][] heights = {{1, 2, 2, 3, 5},
                {3, 2, 3, 4, 4},
                {2, 4, 5, 3, 1},
                {6, 7, 1, 4, 5},
                {5, 1, 1, 2, 4}};
        System.out.println(new Solution_v1().pacificAtlantic(heights));
    }

}